package IC;

import IC.Parser.Lexer;
import IC.Parser.SyntaxError;
import IC.lir.*;

public class CompilerImpl 
{
	private RunParameters _params;
	private IC.AST.Program _programASTNode;
	private IC.AST.Library _libASTNode;
	
	public CompilerImpl(RunParameters param) { _params = param; }
	
	public boolean Compile()
    {
    	IC.Error.ErrorStack.Initialize();

		boolean ret = 
			(DoProgramParsing() && DoLibParsing() && PostParsingStep() && DoSemanticAnalysis()
				&& PrintSymbolTable() && PrintAST() && DoCodeGeneration()); //these functions check the RunParameters
    
		if (!ret)
			System.out.println("The compilation process was stopped because of an error.");
    
		return ret;
    }
	
	private boolean DoCodeGeneration()
	{
		Lir lir = CodeGenerationImpl.GenerateCode(_programASTNode, _params.IsOptLir());
		if (_params.IsPrintLir())
			return CodeGenerationImpl.PrintLir(lir, _params.GetFilePath().substring(0, _params.GetFilePath().lastIndexOf('.')) + ".lir");
		return true;
	}
	
	private boolean DoProgramParsing()
	{
    	return (null != (_programASTNode = (IC.AST.Program)Parse(_params.GetFilePath(), false)));
	}
	
	private boolean DoLibParsing()
	{
		return _params.GetLibPath() == null 
			|| 
				(null != (_libASTNode = (IC.AST.Library)Parse(_params.GetLibPath(), true)));
	}
	
	private IC.AST.ASTNode Parse(String file, boolean lib)
	{
		if (file == null)
			return null;
		
		System.out.println("Parsing " + file);
   	 	IC.AST.ASTNode ret = ParseHelper(file, lib);
		if (null != ret)
   	 		System.out.format("Parsed %s successfully!\n", file);
   	 	
   	 	return ret;
	}
	
	private boolean PostParsingStep()
	{
		if (!CheckLibName())
			IC.Error.ErrorStack.Push(
					new IC.Error.ErrorString(
							IC.Error.Error.ErrorType.Semantic,
							"Can't link library since the class name is not 'Library'"));
		else
			_programASTNode.SetLibrary(_libASTNode);
		return true;
	}
	
	private boolean CheckLibName()
	{
		if (_libASTNode != null)
			return _libASTNode.GetLibName().equals("Library");
		return true;
	}
	
    private static IC.AST.ASTNode ParseHelper(String fileName, boolean isLibrary)
    {
    	java.io.Reader reader = Utils.GetReader(fileName);
    	if (reader == null)
    	{
    		System.out.println("Couldn't open file: " + fileName);
    		return null;
    	}
		
    	java.util.Stack<SyntaxError> errorStack = new java.util.Stack<SyntaxError>();
		
		try
		{
			IC.AST.ASTNode root = (IC.AST.ASTNode) 
									( isLibrary ? 
											(new IC.Parser.LibraryParser(new Lexer(reader), errorStack).parse().value)
										: 
											(new IC.Parser.Parser(new Lexer(reader), errorStack).parse().value));
			Utils.CloseReader(reader);
			
			return root;
		}
		catch (IC.Parser.LexicalError e) { System.out.println(e.getMessage()); }
		catch (java.lang.Exception e)
		{
			Utils.CloseReader(reader);
			PrintErrorStack(errorStack, fileName);
		}
		
		return null;
    }
    
    private static void PrintErrorStack(java.util.Stack<SyntaxError> errorStack, String fileName)
    {
    	try
		{
			for (SyntaxError error : errorStack)
			{
				System.out.format("Syntax Error, unexpected token=%s at line=%d\n"
						, error.GetToken().GetMatch(), error.GetToken().GetLine() - 1);
				System.out.println(Utils.LineFromFile(fileName, error.GetToken().GetLine() - 1));
				Utils.DrawArrow(error.GetToken().GetColumn());
			}
		}
		catch (java.io.IOException ex)
		{
			System.out.println("Encountered IO error while printing error message.");
		}
    }
    
    private boolean DoSemanticAnalysis()
    {
    	System.out.println("Performing the Semantic Analysis:");
    	
    	IC.AST.SemanticAnalysis.SemanticAnalysis.DoSemanticAnalysis(_programASTNode);
    	
    	IC.Error.ErrorStack.SortErrorsByLine();
    	for (IC.Error.Error error : IC.Error.ErrorStack.Errors)
    		error.Visit(new IC.Error.ErrorStackVisitor());

    	for (IC.Error.Error error : IC.Error.ErrorStack.Warrnings)
    		error.Visit(new IC.Error.ErrorStackVisitor());
    	
    	if (IC.Error.ErrorStack.Errors.empty())
    		System.out.println("Did you know an apple has 101 calories on avrage? (and btw semantic analysis passed successfully).");
    	
    	
    	return IC.Error.ErrorStack.Errors.empty();
    }
    
    private boolean PrintAST()
    {
    	if (_params.IsPrintAST())
    		_programASTNode.Visit(new IC.AST.PrintVisitor());
    	
    	return true;
    }

    private boolean PrintSymbolTable()
    {
    	if (_params.IsPrintSymTable())
    		System.out.print(_programASTNode.GetSymbolTable().toString());
    	
    	return true;
    }
}
